/**
 * Copyright (C) 2008+ Spark Zheng
 *
 * @file	include/log.h
 * @brief
 *      A thread-safe log modle
 *      We May not use "log.h" directly, perfer to create an **_log.h,
 *      define LOG_NUM and log_arg in **_log.h.
 *
 * @see		dlib
 * @history
 *      version 0.0.1   Spark Zheng init log module, add syslog/streamlog
 *      version 0.0.2   Spark Zheng fixuped ctime bug
 */

#ifndef _DLIB_LOG_H
#define _DLIB_LOG_H

#ifdef __cplusplus
extern "C" {
#endif

#ifndef _LARGEFILE_SOURCE
#define _LARGEFILE_SOURCE
#endif
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#ifdef _FILE_OFFSET_BITS
#undef _FILE_OFFSET_BITS
#endif
#define _FILE_OFFSET_BITS	64

#include "global.h"

#include <stdio.h>
#include <string.h>

#ifdef _DLIB_LOG_USE_SYSLOG
#include <syslog.h>
#else
#include <time.h>
#include "lock.h"
#endif

/** IDENT chars */
#define IDENT_LEN_MAX	15U

/** LOG structs */
typedef struct dlib_log_args {
	uint32_t	log_index;
	char		*log_file;
} dlib_log_args_t;

typedef struct dlib_log {
	char		ident[IDENT_LEN_MAX + 1];

#ifdef _DLIB_LOG_USE_SYSLOG
#define DLOG_OPEN(n, ident, file)	do { \
	memcpy(dlog.ident, ident, IDENT_LEN_MAX); \
	dlog.ident[IDENT_LEN_MAX] = '\0'; \
	openlog(dlog.ident, LOG_CONS|LOG_PID|LOG_NDELAY, LOG_LOCAL7); \
} while (0)
#define DLOG_CLOSE(n)			closelog()

#define DLOG_EMERG(n, fmt, arg...)	syslog(LOG_EMERG|LOG_LOCAL7, \
	"%s[%d]: "#fmt, __func__, __LINE__, ##arg)
#define DLOG_ALERT(n, fmt, arg...)	syslog(LOG_ALERT|LOG_LOCAL7, \
	"%s[%d]: "#fmt, __func__, __LINE__, ##arg)
#define DLOG_CRIT(n, fmt, arg...)	syslog(LOG_CRIT|LOG_LOCAL7, \
	"%s[%d]: "#fmt, __func__, __LINE__, ##arg)
#define DLOG_ERR(n, fmt, arg...)	syslog(LOG_ERR|LOG_LOCAL7, \
	"%s[%d]: "#fmt, __func__, __LINE__, ##arg)
#define DLOG_WARNING(n, fmt, arg...)	syslog(LOG_WARNING|LOG_LOCAL7, \
	"%s[%d]: "#fmt, __func__, __LINE__, ##arg)
#define DLOG_NOTICE(n, fmt, arg...)	syslog(LOG_NOTICE|LOG_LOCAL7, \
	"%s[%d]: "#fmt, __func__, __LINE__, ##arg)
#define DLOG_INFO(n, fmt, arg...)	syslog(LOG_INFO|LOG_LOCAL7, \
	"%s[%d]: "#fmt, __func__, __LINE__, ##arg)
#ifdef _DEBUG
#define DLOG_DEBUG(n, fmt, arg...)	syslog(LOG_INFO|LOG_LOCAL7, \
	"%s[%d]: "#fmt, __func__, __LINE__, ##arg)
#else
#define DLOG_DEBUG(n, fmt, arg...)	({ \
	if(0) syslog(LOG_INFO|LOG_LOCAL7, "%s[%d]: "#fmt, __func__, \
		__LINE__, ##arg); 0;})
#endif /* end _DEBUG */

#else /* not use syslog */
#define TIMESTR_LEN_MAX	31U
	time_t	  t;
	char		t_str[TIMESTR_LEN_MAX + 1];
	FILE		**fp;
	dlib_lock_t	*locks;
/**	
 * Please check the validity of the result return by fopen.
 * Add: if(dlog.fp[n] == NULL) { ...error_handler; }
 */
#define DLOG_OPEN(n, ident, file)	do { \
	dlog.fp[n] = fopen(file, "a"); \
	memcpy(dlog.ident, ident, IDENT_LEN_MAX); \
	dlog.ident[IDENT_LEN_MAX] = '\0'; } while(0)
#define DLOG_CLOSE(n)			do { \
	if(dlog.fp[n] != NULL) fclose(dlog.fp[n]); } while (0)

/**
 * Need to avaid deadlock by yourself.
 * Foreach fd related by a mutex lock, when read/write, check the lock first.
 */
#define DLOG_LOCK(n)			do { \
	dlib_lock_lock(&dlog.locks[n]); } while (0)
#define DLOG_UNLOCK(n)			do { \
	dlib_lock_unlock(&dlog.locks[n]); } while (0)

#define DLOG_EMERG(n, fmt, arg...)	do { \
	DLOG_LOCK(n); \
	dlog.t = time(NULL); \
	strftime(dlog.t_str, TIMESTR_LEN_MAX+1, "%Y-%m-%d %H:%M:%S %s ",\
		 localtime(&dlog.t)); \
	fprintf(dlog.fp[n], "%s%s EMERG: %s[%d]: "#fmt"\n", \
		dlog.t_str, dlog.ident, __func__, __LINE__, ##arg); \
	fflush(dlog.fp[n]); \
	DLOG_UNLOCK(n); } while (0)
#define DLOG_ALERT(n, fmt, arg...)	do { \
	DLOG_LOCK(n); \
	dlog.t = time(NULL); \
	strftime(dlog.t_str, TIMESTR_LEN_MAX+1, "%Y-%m-%d %H:%M:%S %s ",\
		 localtime(&dlog.t)); \
	fprintf(dlog.fp[n], "%s%s ALERT: %s[%d]: "#fmt"\n", \
		dlog.t_str, dlog.ident, __func__, __LINE__, ##arg); \
	fflush(dlog.fp[n]); \
	DLOG_UNLOCK(n); } while (0)
#define DLOG_CRIT(n, fmt, arg...)	do { \
	DLOG_LOCK(n); \
	dlog.t = time(NULL); \
	strftime(dlog.t_str, TIMESTR_LEN_MAX+1, "%Y-%m-%d %H:%M:%S %s ",\
		 localtime(&dlog.t)); \
	fprintf(dlog.fp[n], "%s%s CRIT: %s[%d]: "#fmt"\n", \
		dlog.t_str, dlog.ident, __func__, __LINE__, ##arg); \
	fflush(dlog.fp[n]); \
	DLOG_UNLOCK(n); } while (0)
#define DLOG_ERR(n, fmt, arg...)	do { \
	DLOG_LOCK(n); \
	dlog.t = time(NULL); \
	strftime(dlog.t_str, TIMESTR_LEN_MAX+1, "%Y-%m-%d %H:%M:%S %s ",\
		 localtime(&dlog.t)); \
	fprintf(dlog.fp[n], "%s%s ERR: %s[%d]: "#fmt"\n", \
		dlog.t_str, dlog.ident, __func__, __LINE__, ##arg); \
	fflush(dlog.fp[n]); \
	DLOG_UNLOCK(n); } while (0)
#define DLOG_WARNING(n, fmt, arg...)	do { \
	DLOG_LOCK(n); \
	dlog.t = time(NULL); \
	strftime(dlog.t_str, TIMESTR_LEN_MAX+1, "%Y-%m-%d %H:%M:%S %s ",\
		 localtime(&dlog.t)); \
	fprintf(dlog.fp[n], "%s%s WARNING: %s[%d]: "#fmt"\n", \
		dlog.t_str, dlog.ident, __func__, __LINE__, ##arg); \
	fflush(dlog.fp[n]); \
	DLOG_UNLOCK(n); } while (0)
#define DLOG_NOTICE(n, fmt, arg...)	do { \
	DLOG_LOCK(n); \
	dlog.t = time(NULL); \
	strftime(dlog.t_str, TIMESTR_LEN_MAX+1, "%Y-%m-%d %H:%M:%S %s ",\
		 localtime(&dlog.t)); \
	fprintf(dlog.fp[n], "%s%s NOTICE: %s[%d]: "#fmt"\n", \
		dlog.t_str, dlog.ident, __func__, __LINE__, ##arg); \
	fflush(dlog.fp[n]); \
	DLOG_UNLOCK(n); } while (0)
#define DLOG_INFO(n, fmt, arg...)	do { \
	DLOG_LOCK(n); \
	dlog.t = time(NULL); \
	strftime(dlog.t_str, TIMESTR_LEN_MAX+1, "%Y-%m-%d %H:%M:%S %s ",\
		 localtime(&dlog.t)); \
	fprintf(dlog.fp[n], "%s%s INFO: %s[%d]: "#fmt"\n", \
		dlog.t_str, dlog.ident, __func__, __LINE__, ##arg); \
	fflush(dlog.fp[n]); \
	DLOG_UNLOCK(n); } while (0)
#ifdef _DEBUG
#define DLOG_DEBUG(n, fmt, arg...)	do { \
	DLOG_LOCK(n); \
	dlog.t = time(NULL); \
	strftime(dlog.t_str, TIMESTR_LEN_MAX+1, "%Y-%m-%d %H:%M:%S %s ",\
		 localtime(&dlog.t)); \
	fprintf(dlog.fp[n], "%s%s DEBUG: %s[%d]: "#fmt"\n", \
		dlog.t_str, dlog.ident, __func__, __LINE__, ##arg); \
	fflush(dlog.fp[n]); \
	DLOG_UNLOCK(n); } while (0)
#else
#define DLOG_DEBUG(n, fmt, arg...)
#endif
#endif
} dlib_log_t;

/** global struct defined in log.c */
extern dlib_log_t dlog;

/**
 * dlib_log_init
 * @brief be aware of your privillege.
 * @param path: the base dir stored log/logs
 * @param ident: the log ident string. not longer than IDENT_LEN_MAX(15u)
 * @param num: the arg number
 * @param args: the array name of log_args-array
 */
extern int dlib_log_init(const char *ident, const char *path, uint32_t n,
			 dlib_log_args_t *args);

/** dlib_log_exit */
extern void dlib_log_exit(uint32_t n);

#ifdef __cplusplus
}
#endif
#endif
